﻿// Brandon Haynes - http://brandonhaynes.org
// Copyright (c) 2008
// by Brandon Haynes

// Please refer to the project license, located at http://www.codeplex.com/DNNLocalization/license

// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.

//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
//INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
//WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
//USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Resources;
using System.Resources.Tools;
using System.Security.Permissions;
using System.Web.Compilation;
using System.Xml;
using System.Xml.Schema;

namespace BrandonHaynes.DotNetNukeResXBuildProvider
	{
	public abstract class BaseResourcesBuildProvider : BuildProvider
		{
		#region Protected Properties
		protected bool GenerateStronglyTypedClasses { get; set; }
		protected ResourcePathMetadata Resource { get; set; }
		#endregion

		#region Constructors
		protected BaseResourcesBuildProvider()
			{ GenerateStronglyTypedClasses = true; }
		#endregion

		#region Abstract Members
		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
		protected abstract IResourceReader GetResourceReader(Stream inputStream);
		#endregion

		#region Public Methods

		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
		public override void GenerateCode(AssemblyBuilder assemblyBuilder)
			{
			Resource = new ResourcePathMetadata(VirtualPath);

			using (Stream stream = base.OpenStream())
			    {
				var resourceReader = this.GetResourceReader(stream);

				try
					{
					GenerateResourceFile(assemblyBuilder, resourceReader);
					}
				catch (ArgumentException exception)
					{
					throw DereferenceException(exception);
					}

				if (Resource.Culture.Equals(CultureInfo.InvariantCulture) && !Resource.IsLocal && GenerateStronglyTypedClasses)
					GenerateStronglyTypedClass(assemblyBuilder, resourceReader);
			    }
			}

		#endregion

		#region Private Methods

		private void GenerateResourceFile(AssemblyBuilder assemblyBuilder, IResourceReader reader)
			{
			using (var stream = assemblyBuilder.CreateEmbeddedResource(this, Resource.EmbeddedName))
			using (var writer = new ResourceWriter(stream))
				foreach (DictionaryEntry entry in reader)
					writer.AddResource(entry.Key.ToString(), entry.Value);
			}

		private void GenerateStronglyTypedClass(AssemblyBuilder assemblyBuilder, IResourceReader reader)
			{
			IDictionary resourceList;
			string[] unmatchableElements;

			using (reader)
				resourceList = GetResourceList(reader);

			var code = StronglyTypedResourceBuilder.Create(resourceList, Resource.TypeName, Resource.Namespace, assemblyBuilder.CodeDomProvider, false, out unmatchableElements);
			assemblyBuilder.AddCodeCompileUnit(this, code.Adapt(Resource));
			}

		private static IDictionary GetResourceList(IResourceReader reader)
			{
			return reader
				.Cast<DictionaryEntry>()
				.ToDictionary(entry => entry.Key, entry => entry.Value);
			}

		private static Exception DereferenceException(Exception exception)
			{
			if (exception.InnerException == null || (!(exception.InnerException is XmlException) && !(exception.InnerException is XmlSchemaException)))
				return exception;

			return exception.InnerException;
			}

		#endregion
		}
	}
